package com.leyantech.ecp.agenx.common.misc;

import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.VertxException;
import io.vertx.core.shareddata.Lock;
import io.vertx.spi.cluster.zookeeper.impl.ZKLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author eahau, {@literal <eahau@foxmail.com>}
 * @date 2022-12-12.
 */
@Slf4j
public class ZkLockUtils {

  @Inject
  private static Vertx vertx;

  @Inject
  private static CuratorFramework curator;

  @Inject
  @Named("lockReleaseExec")
  private static ExecutorService lockReleaseExec;

  /**
   * <pre>
   *   考虑到 {@link io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager#getLockWithTimeout }
   *   会将 lock 缓存到 {@link io.vertx.spi.cluster.zookeeper.ZookeeperClusterManager#locks}
   *   并且释放锁时不会删除该对象，所以自定义该方法.
   * </pre>
   */
  public static Future<Lock> getLock(String name, long waitMills) {
    return vertx.executeBlocking(promise -> {
      final InterProcessSemaphoreMutex lock = new InterProcessSemaphoreMutex(
          curator, "/locks/" + name);
      try {
        if (lock.acquire(waitMills, TimeUnit.MILLISECONDS)) {
          promise.complete(new ZKLock(lock, lockReleaseExec));
        } else {
          promise.fail("Timed out waiting to get lock " + name);
        }
      } catch (Exception e) {
        promise.fail(new VertxException("get lock " + name + "exception", e));
      }
    }, false);
  }

}
